<template>
    <div 
        :class="[
            'app-editor-container',
            'app-cascader',
			`app-cascader-${name}`,
            `app-cascader--${editorStyle}`
        ]"
    >
        <template v-if="editorStyle == 'tree'">
            <a-tree-select
                :size="size"
                :class="['app-editor-container']"
                treeNodeFilterProp="label"
                v-model:value="selectValue"
                :placeholder="placeholder"
                :allowClear="clearable"
                :tree-data="treeData"
                :load-data="treeLoadData"
                :disabled="disabled || readonly"
                :tree-checkable="multiple"
                :filterTreeNode="filterable"
                :showSearch="filterable"
                :fieldNames="{ children: 'children', label: 'label', value: 'nodeId' }"
                @change="handleTreeValueChange">
                <template #tagRender="{ label, closable, onClose, option }">
                    <a-tag :class="`app-cascader--${editorStyle}-tag`" :closable="closable" @close="onClose">
                        {{ option.plabel.join(separator) + separator + label }}
                    </a-tag>
                </template>
                <template #title="node">
                    <!-- active 属性用于判断 是树的title插槽还是select的单选插槽 -->
                    <template v-if="node.hasOwnProperty('active')">
                        {{ node.label }}
                    </template>
                    <template v-else>
                        {{ node.plabel ? node.plabel.join(separator) + separator + node.label : node.label }}
                    </template>
                </template>
            </a-tree-select>
        </template>
        <template v-else>
            <a-cascader
                :class="['app-editor-container']"
                :size="size"
                v-model:value="selectValue"
                :disabled="disabled || readonly"
                :multiple="multiple"
                :load-data="cascaderLoadData"
                :allowClear="clearable"
                :options="treeData"
                :show-search="{ filter }"
                :fieldNames="{ children: 'children', label: 'label', value: 'value' }"
                :placeholder="placeholder"
                showCheckedStrategy="SHOW_CHILD"
                @change="handleCascaderValueChange">
                <template #displayRender="{ labels }">
                    <template v-if="!multiple">
                        {{labels ? labels.join(separator) : null}}
                    </template>
                    <template v-else>
                        <template v-for="(label, index) in labels">
                            {{label}}{{index !== labels.length - 1 ? separator : null}}
                        </template>
                    </template>
                </template>
            </a-cascader>
        </template>
    </div>
</template>
<script setup lang="ts">
import { deepCopy, IEntityDetail, IEvent, IParam, typeOf, createUUID } from '@/core';
import { isFunc } from 'qx-util';
import { Ref } from 'vue';
import { CascaderProps, ValueItemType } from './cascader';
import type { ShowSearchType } from 'ant-design-vue/es/cascader';
import { useEditorTypeParams } from '@/hooks/use-editor';
const props = defineProps(CascaderProps);
interface EditorEmit {
	(name: 'component-action', value: IEvent<string>): void;
}
const emit = defineEmits<EditorEmit>();
// 类型校验
const { validateConfig } = useEditorTypeParams();
validateConfig();
/**
 * 树数据
 */
const treeData: Ref<any[]> = ref([]);
/**
 * 数据集
 */
const items: any[] = [];
/**
 * 选中数据
 */
let selectValue: Ref<any> = ref(props.multiple ? [] : '');
/**
 * 初始化
 */
let init: boolean = false;

watch(
	() => props.value,
	(newVal: any, oldVal: any) => {
        if (!init) {
            init = true;
            if (newVal) {
                handleSelectData();
            }
            loadData(props.valueItems[0]).then((_treeData: IParam[]) => {
                if (_treeData) {
                    if (treeData.value.length > 0) {
                        _treeData.forEach((node: IParam) => {
                            const index = treeData.value.findIndex((_node: any) => node.value == _node.value);
                            if (index > -1) {
                                Object.assign(treeData.value[index], node);
                            } else {
                                treeData.value.push(node);
                            }
                        })
                    } else {
                        treeData.value = _treeData;
                    }
                }
		    }); 
        }
	},
);

/**
 * @description 级联选择过滤
 * @param inputValue 输入值
 * @param path 路径
 */
const filter: ShowSearchType['filter'] = (inputValue: string, path: any[]) => {
    return path.some((option: any) => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
};

/**
 * @description 获取树数据
 * @param nodes  树数据
 * @param parentKey 父数据
 * @param childNodes 子节点数据集合
 */
const getTreeData = (nodes: IParam[], parentKey: string, childNodes: IParam[]) => {
    if (nodes && nodes.length > 0) {
        nodes.forEach((node: any) => {
            if (node.value == parentKey) {
                childNodes = node.children || [];
            } else {
                getTreeData(node.children, parentKey, childNodes);
            }
        })
    } else {
        childNodes = [];
    }
}

/**
 * @description 设置树数据
 * @param treeData 树数据
 * @param treePath 树路径
 * @param index 树层级索引
 */
const setTreeData = (treeData: IParam[], treePath: string[], index: number, parent?: any) => {
    if (index > -1 && index < treePath.length) {
        const item = items.find((item: IParam) => item.value == treePath[index]);
        const treeItem = treeData.find((item: IParam) => item.value == treePath[index]);
        if (!treeItem) {
            const children: IParam = { 
                label: item.label, 
                value: item.value, 
                children: [], 
                plabel: index ? [...parent.plabel, parent.label] : [],
                pvalue: index ? [...parent.pvalue, parent.value] : [],
                isLeaf: index == treePath.length - 1, 
                nodeId: `${parent ? parent.value + "_" + item.value : item.value}`,
                index 
            };
            setTreeData(children.children, treePath, index + 1, children);
            treeData.push(children);
        } else {
            if (!treeItem.children) {
                treeItem.children = [];
            }
            setTreeData(treeItem.children, treePath, index + 1, treeItem);
        }
    }
}

/**
 * @description 处理选中数据
 */
const handleSelectData = () => {
    if (props.value && props.valueItems.length > 0) {
        const values: string[] = JSON.parse(props.value);
        const selectData: IParam[] = [];
        const valueItemData: IParam[] = [];
        props.valueItems.forEach((valueItem: any) => {
            valueItemData.push({ name: valueItem.name, value: props.data[valueItem.name] ? props.data[valueItem.name].split(',') : [] });
        });
        values.forEach((label: string, index: number) => {
            const value: string [] = [];
            valueItemData.forEach((item: IParam) => {
                value.push(item.value[index]);
            })
            selectData.push({ label, value: value.join(props.separator) });
        });
        selectData.forEach((select: IParam) => {
            const _values: any[] = select.value.split(props.separator);
            const _labels: any[] = select.label.split(props.separator);
            _values.forEach((value: string, index: number) => {
                const item = items.find((item: any) => item.value == value);
                if (!item) {
                    items.push({ value, label: _labels[index] })
                }
            })
        })
        selectValue.value = [];
        let tempSelectValue: any[] = [];
        let tempTreeSelectValue: any;
        if (!props.multiple) {
            tempTreeSelectValue = '';
            tempSelectValue = selectData[0].value.split(props.separator);
            tempTreeSelectValue = tempSelectValue.length > 1 ? 
                tempSelectValue[tempSelectValue.length - 2] + "_" + tempSelectValue[tempSelectValue.length - 1] :
                tempSelectValue[tempSelectValue.length - 1]
            setTreeData(treeData.value, tempSelectValue, 0);
        } else {
            tempTreeSelectValue = [];
            selectData.forEach((select: IParam) => {
                tempSelectValue.push(select.value.split(props.separator));
            })
            tempSelectValue.forEach((select: string[]) => {
                setTreeData(treeData.value, select, 0);
                tempTreeSelectValue.push(
                    select.length > 1 ? 
                        select[select.length - 2] + "_" + select[select.length - 1] :
                        select[select.length - 1]
                )
            })
        }
        selectValue.value = Object.is(props.editorStyle, 'tree') ? tempTreeSelectValue : tempSelectValue;
    }
};

/**
 * @description 处理级联数据改变
 */
const handleCascaderValueChange = () => {
	// 计算值项选中数据
	const valueItemData: any[] = [];
	props.valueItems.forEach((valueItem: any) => {
		valueItemData.push({ name: valueItem.name, value: [] });
	});
	selectValue.value?.forEach((item: any, index: number) => {
		if (!props.multiple) {
			valueItemData[index].value.push(item);
		} else {
			item.forEach((_item: any, _index: number) => {
				valueItemData[_index].value.push(_item);
			});
		}
	});
	// 计算当前选中数据文本
	const curSelectPath: any[] = [];
	let curSelectText: string[] = [];
	selectValue.value?.forEach((selected: any) => {
		if (!props.multiple) {
			const selectItem = items.find((item: any) => item.value == selected);
			if (selectItem) {
				curSelectPath.push(selectItem.label);
			}
		} else {
			const selectItems: string[] = selected.map((select: string) => {
				const selectItem = items.find((item: any) => item.value == select);
				if (selectItem) {
					return selectItem.label;
				}
			});
			curSelectPath.push(selectItems);
		}
	});
	valueItemData.forEach((valueItem: any) => {
		emit('component-action', {
			name: valueItem.name,
			action: 'valueChange',
			data: valueItem.value.length > 0 ? valueItem.value.join(',') : null,
		});
	});
	if (curSelectPath.length > 0) {
        if (props.multiple) {
            curSelectPath.forEach((path: string[]) => {
                curSelectText.push(path.join(props.separator));
            })
        } else {
            curSelectText = [curSelectPath.join(props.separator)]
        }
	}
	emit('component-action', {
		name: props.name as string,
		action: 'valueChange',
		data: curSelectText.length > 0 ? JSON.stringify(curSelectText) : null,
	});
};

/**
 * @description 处理树选中改变
 * @param curValue 选中值
 * @param curLabel 选中文本
 * @param extra 选中参数
 */
const handleTreeValueChange = (curValue: string | string[], curLabel: string | string[], extra: IParam) => {
    const valueItemData: any[] = [];
    let curSelectText: string[] = [];
	props.valueItems.forEach((valueItem: any) => {
		valueItemData.push({ name: valueItem.name, value: [] });
	});
    if (curValue && curValue.length > 0) {
        const { allCheckedNodes, triggerNode, checked } = extra;
        if (props.multiple) {
            allCheckedNodes.forEach((item: IParam) => {
                // 注意 树多选添加选中和删除选中allCheckedNodes的数据结构有差异
                const { value, label, pvalue, plabel } = checked ? item.node.props : item.props;
                const values: string[] = [...pvalue, value];
                const labels: string[] = [...plabel, label];
                valueItemData.forEach((valueItem: IParam, index: number) => {
                    valueItem.value.push(values[index]);
                })
                curSelectText.push(labels.join(props.separator));
            })
        } else {
            const { value, label, pvalue, plabel } = triggerNode.props;
            const values: string[] = [...pvalue, value];
            const labels: string[] = [...plabel, label];
            valueItemData.forEach((valueItem: IParam, index: number) => {
                valueItem.value.push(values[index]);
            })
            curSelectText = [labels.join(props.separator)];
        }
    }
    valueItemData.forEach((valueItem: any) => {
		emit('component-action', {
			name: valueItem.name,
			action: 'valueChange',
			data: valueItem.value.length > 0 ? valueItem.value.join(',') : null,
		});
	});
    emit('component-action', {
		name: props.name as string,
		action: 'valueChange',
		data: curSelectText.length > 0 ? JSON.stringify(curSelectText) : null,
	});
};

/**
 * @description 处理加载数据（去重）
 * @param data 加载数据
 * @param pvalue 父数据
 */
const handleLoadData = (data: IParam[], pvalue: string): IParam[] => {
    const tempData: IParam[] = [];
    getTreeData(treeData.value, pvalue, tempData);
    data.forEach((item: IParam) => {
        const _index = tempData.findIndex((_item: any) => item.value == _item.value);
        if (_index > 0) {
            Object.assign(tempData[_index], item);
        } else {
            tempData.push(item);
        }
    })
    return tempData;
}

/**
 * @description 级联选择器加载数据
 * @param selectedOptions 选中节点配置
 */
const cascaderLoadData = async (selectedOptions: any) => {
	const targetOption = selectedOptions[selectedOptions.length - 1];
	targetOption.loading = true;
	const { index, value, pvalue, plabel, label } = targetOption;
    const _pvalue = [...pvalue, value];
    const _plabel = [...plabel, label];
	const data: IParam[] = await loadData(props.valueItems[index + 1], index + 1, _pvalue, _plabel);
    const tempData: IParam[] = handleLoadData(data, value);
	targetOption.loading = false;
	targetOption.children = tempData;
	treeData.value = [...treeData.value];
};

/**
 * @description 树加载数据
 * @param selectedOptions 选中节点配置
 */
const treeLoadData = (selectedOptions: any) => {
	return new Promise(async (resolve) => {
        selectedOptions.loading = true;
		const { index, value, pvalue, plabel, label } = selectedOptions.dataRef;
        const _pvalue = [...pvalue, value];
        const _plabel = [...plabel, label];
		const data = await loadData(props.valueItems[index + 1], index + 1, _pvalue, _plabel);
        const tempData: IParam[] = handleLoadData(data, value);
		selectedOptions.loading = false;
		selectedOptions.dataRef.children = tempData;
		treeData.value = [...treeData.value];
		resolve(true);
	});
};

/**
 * @description 处理请求参数
 * @param index 树层级索引
 * @param pvalue 父数据值
 */
const handleQueryParams = (
	index: number,
	pvalue: string[],
) => {
	const context = deepCopy(props.context);
	const viewParams = deepCopy(props.viewParams);
    const entityDetails = App.getAppEntityConfig();
	if (index > 0 && pvalue.length > 0) {
        const pValueItem: ValueItemType = props.valueItems[index - 1];
        const pDataEntity: IEntityDetail | undefined = entityDetails.find(
            (detail: IEntityDetail) => {
                return detail.codeName == pValueItem.appDataEntity;
            }
        );
        if (pDataEntity) {
            Object.assign(context, {
                [pDataEntity.codeName.toLowerCase()]: pvalue[pvalue.length - 1],
            });
            Object.assign(viewParams, {
                [`n_${pDataEntity.keyField.toLowerCase()}_eq`]: pvalue[pvalue.length - 1],
            });
        }
	}
	return { context, viewParams };
};

/**
 * @description 加载数据
 * @param valueItem 值项
 * @param index 树层级索引
 * @param pvalue 父数据值集合
 */
const loadData = async (
	valueItem: ValueItemType,
	index: number = 0,
	pvalue: string[] = [],
    plabel: string[] = [],
): Promise<IParam[]> => {
	const entityDetails = App.getAppEntityConfig();
    let data: IParam[] = [];
	const valueItemDataEntity: IEntityDetail | undefined = entityDetails.find(
		(detail: IEntityDetail) => {
			return detail.codeName == valueItem.appDataEntity;
		}
	);
	// 未配置属性关联实体 和 数据集
	if (!valueItemDataEntity || !valueItem.dataSet) {
		return data;
	}
	const service = await App.getDataService(valueItem.appDataEntity, props.context);
	// 关联实体服务不存在 不加载数据
	if (!service || isFunc(service[valueItem.dataSet])) {
		return data;
	}
	const { context, viewParams } = handleQueryParams(index, pvalue);
	const response = await service[valueItem.dataSet](context, viewParams);
	if (response.success) {
        data = response.data.map((item: any) => {
            const value = item[valueItemDataEntity.keyField.toLowerCase()];
            const label = item[valueItemDataEntity.majorField.toLowerCase()];
            return {
                value,
                label,
                plabel,
                pvalue,
                isLeaf: index == props.valueItems.length - 1,
                selectable: index == props.valueItems.length - 1,
                disableCheckbox: Object.is(props.editorStyle, 'tree') ?  index != props.valueItems.length - 1 : false,
                nodeId: `${pvalue.length > 0 ? pvalue[pvalue.length - 1] + '_' + value : value }`,
                index,
            };
        });
        data.forEach((node: any) => {
            const index = items.findIndex((item: any) => node.value == item.value);
            if (index != -1) {
                items[index] = node;
            } else {
                items.push(node);
            }
        });
	}
	return data;
};
</script>
